package com.le.enhance.class1;

import org.junit.Test;

/**
 * 寻找数组中第[k]小的数
 * bfprt算法
 * 1. 分组 --> 按 3 / 5 /7分
 * 2. 每组排序
 * 3. 获取每组的中位数->数组, 获取数组中位数
 * 4. 调用bfprt
 */
public class Code_03_bfprt {

    public static int getMinKByBFPRT(int[] arr, int k) {
        int[] res = copyArray(arr);
        return bfprt(res, 0, res.length - 1, k - 1);
    }

    private static int[] copyArray(int[] arr) {
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }


    public static int bfprt(int[] arr, int begin, int end, int i) {
        if (begin == end) {
            return arr[begin];
        }
        int pivot = medianOfdians(arr, begin, end); // 寻找中位数组的中位值
        int[] pivotRange = partition(arr, begin, end, pivot); // 荷兰国旗问题partition
        if (i >= pivotRange[0] && i <= pivotRange[1]) {
            return arr[i];
        } else if (i < pivotRange[0]) {
            return bfprt(arr, begin, pivotRange[0] - 1, i);
        } else {
            return bfprt(arr, pivotRange[1] + 1, end, i);
        }
    }

    // 荷兰国旗问题返回 相等元素存在位置
    private static int[] partition(int[] arr, int begin, int end, int pivot) {
        int small = begin - 1;
        int cur = begin;
        int big = end + 1;
        while (cur != big) {
            if (arr[cur] < pivot) {
                swap(arr, ++small, cur++);
            } else if (arr[cur] > pivot) {
                swap(arr, --big, cur);
            } else {
                cur++;
            }
        }
        int[] range = new int[2];
        range[0] = small + 1;
        range[1] = big - 1;
        return range;
    }

    private static int medianOfdians(int[] arr, int begin, int end) {
        int num = end - begin + 1;
        int offset = num % 5 == 0 ? 0 : 1;
        int[] mArr = new int[num / 5 + offset]; // 中维数组
        for (int i = 0; i < mArr.length; i++) {
            int beginI = begin + i * 5; // 5个数一组;0--4,5-9
            int endI = beginI + 4;
            mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
        }
        return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2);
    }


    // 获取每组的中位数
    private static int getMedian(int[] arr, int begin, int end) {
        insertionSort(arr, begin, end);
        int sum = end + begin;
        int mid = (sum / 2) + (sum % 2); // 取上中位数
        return arr[mid];
    }

    private static void insertionSort(int[] arr, int begin, int end) {
        for (int i = begin + 1; i != end + 1; i++) {
            for (int j = i; j != begin; j--) {
                if (arr[j - 1] > arr[j]) {
                    swap(arr, j - 1, j);
                } else {
                    break;
                }
            }
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    @Test
    public void test() {
        int[] arr = {3, 5, 6, 2, 3, 1};
        int minKByBFPRT = getMinKByBFPRT(arr, 2);
        System.out.println(minKByBFPRT);
    }
}
